﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DataSourcesFile;
using ESRI.ArcGIS.DataSourcesRaster;
using FRS.ExceptionLog;

namespace FRS.TinProcessor
{
    public static class TinCreator
    {
        /// <summary>
        /// 通过矢量图层创建Tin数据，并保存在用户指定的路径下
        /// </summary>
        /// <param name="pFeatureclass">矢量图层</param>
        /// <param name="saveTinpath">Tin数据保存的路径</param>
        /// <param name="heightfieldName">矢量图层文件中表示高程的字段名称</param>
        public static void CreateTinFromFeature(IFeatureClass pFeatureclass, string saveTinpath, string heightfieldName)
        {
            IGeoDataset pGeodataset = pFeatureclass as IGeoDataset;
            IEnvelope extent = pGeodataset.Extent;
            extent.SpatialReference = pGeodataset.SpatialReference;

            IFields fields = pFeatureclass.Fields;
            IField heightfield = fields.get_Field(fields.FindField(heightfieldName));

            // 将Geodatabase.dll的Embed Interop Type设为False;
            ITinEdit tinedit = new TinClass();
            tinedit.InitNew(extent);

            try
            {
                tinedit.AddFromFeatureClass(pFeatureclass, null, heightfield, null, esriTinSurfaceType.esriTinMassPoint);
            }
            catch (Exception ex)
            {
                // 此处应该应该调用日志记录类：创建TIN失败。
                ExceptionLogRecorder.WriteExceptionLog(ex, "创建TIN失败\n文件为：" + saveTinpath);
            }

            tinedit.SaveAs(saveTinpath);
            tinedit.StopEditing(false);
        }
        /// <summary>
        /// 由栅格数据创建Tin
        /// </summary>
        /// <param name="pRraster">传入的栅格文件</param>
        /// <param name="saveTinpath">用户指定Tin文件保存的路径</param>
        /// <param name="rasterBandIndex">表示高程的波段，raster文件实质上是一张单波段的影像数据，故默认值为0</param>
        public static void CreateTinFromRaster(IRaster pRraster, string saveTinpath, int rasterBandIndex = 0)
        {
            if (!Directory.Exists(System.IO.Path.GetDirectoryName(saveTinpath)))
            {
                Directory.CreateDirectory(System.IO.Path.GetDirectoryName(saveTinpath));
            }

            double m_zTolerance = 0.001;
            IGeoDataset pGeoData = pRraster as IGeoDataset;
            IEnvelope pExtent = pGeoData.Extent;
            IRasterBandCollection pRasBC = pRraster as IRasterBandCollection;
            IRasterBand pRasBand = pRasBC.Item(rasterBandIndex);  // raster文件实质上是一张单波段的影像数据
            IRawPixels pRawPixels = pRasBand as IRawPixels;
            IRasterProps pProps = pRawPixels as IRasterProps;

            int iWid = pProps.Width;
            int iHei = pProps.Height;

            double w = iWid / 1000.0f;
            double h = iHei / 1000.0f;

            IPnt pBlockSize = new DblPntClass();
            bool IterationFlag;

            if (w < 1 && h < 1) //横纵都小于1000个像素
            {
                pBlockSize.X = iWid;
                pBlockSize.Y = iHei;
                IterationFlag = false;
            }
            else
            {
                pBlockSize.X = 1001.0f;
                pBlockSize.Y = 1001.0f;
                IterationFlag = true;
            }

            double cellsize = 0.0f;     //栅格大小
            IPnt pPnt1 = pProps.MeanCellSize(); //栅格平均大小
            cellsize = pPnt1.X;

            ITinEdit pTinEdit = new TinClass() as ITinEdit;
            pTinEdit.InitNew(pExtent);

            ISpatialReference pSpatial = pGeoData.SpatialReference;
            pExtent.SpatialReference = pSpatial;

            IPnt pOrigin = new DblPntClass();
            IPnt pPixelBlockOrigin = new DblPntClass();

            //栅格左上角像素中心坐标
            double bX = pBlockSize.X;
            double bY = pBlockSize.Y;

            pBlockSize.SetCoords(bX, bY);
            IPixelBlock pPixelBlock = pRawPixels.CreatePixelBlock(pBlockSize);

            object nodata = pProps.NoDataValue;     //无值标记
            ITinAdvanced2 pTinNodeCount = pTinEdit as ITinAdvanced2;
            int nodeCount = pTinNodeCount.NodeCount;

            object vtMissing = Type.Missing;

            object vPixels = null;      //格子
            if (IterationFlag)   //当为一个处理单元格子时
            {
                pPixelBlockOrigin.SetCoords(0.0f, 0.0f);
                pRawPixels.Read(pPixelBlockOrigin, pPixelBlock);

                vPixels = pPixelBlock.get_SafeArray(0);
                double xMin = pExtent.XMin;
                double yMax = pExtent.YMax;
                pOrigin.X = xMin + cellsize / 2;
                pOrigin.Y = yMax - cellsize / 2;
                bX = pOrigin.X;
                bY = pOrigin.Y;

                pTinEdit.AddFromPixelBlock(bX, bY, cellsize, cellsize, nodata, vPixels, m_zTolerance, ref vtMissing, out vtMissing);
            }
            else  //当有多个处理单元格时，依次循环处理每个单元格
            {
                int i = 0, j = 0, count = 0;
                int FirstGoNodeCount = 0;
                while (nodeCount != FirstGoNodeCount)
                {
                    count++;
                    nodeCount = pTinNodeCount.NodeCount;
                    //依次循环处理
                    for (i = 0; i < h + 1; i++)
                    {
                        for (j = 0; j < w + 1; j++)
                        {
                            double bX1, bY1, xMin1, yMax1;
                            bX1 = pBlockSize.X;
                            bY1 = pBlockSize.Y;

                            pPixelBlockOrigin.SetCoords(j * bX1, i * bY1);
                            pRawPixels.Read(pPixelBlockOrigin, pPixelBlock);
                            vPixels = pPixelBlock.get_SafeArray(0);

                            xMin1 = pExtent.XMin;
                            yMax1 = pExtent.YMax;

                            bX1 = pBlockSize.X;
                            bY1 = pBlockSize.Y;

                            pOrigin.X = xMin1 + j * bX1 * cellsize + cellsize / 2.0f;
                            pOrigin.Y = yMax1 + i * bY1 * cellsize - cellsize / 2.0f;

                            bX1 = pOrigin.X;
                            bY1 = pOrigin.Y;

                            pTinEdit.AddFromPixelBlock(bX1, bY1, cellsize, cellsize, nodata, vPixels, m_zTolerance, ref vtMissing, out vtMissing);

                            FirstGoNodeCount = pTinNodeCount.NodeCount;
                        }
                    }
                }
            }

            //保存TIN文件
            pTinEdit.SaveAs(saveTinpath);
            pTinEdit.StopEditing(true);
        }

        public static void CreateTin(this IFeatureClass pFeatureclass, string saveTinpath, string heightfieldName)
        {
            CreateTinFromFeature(pFeatureclass, saveTinpath, heightfieldName);
        }
        public static void CreateTin(this IRaster pRraster, string saveTinpath, int rasterBandIndex = 0)
        {
            CreateTinFromRaster(pRraster, saveTinpath, rasterBandIndex);
        }
    }
}
